using System;
using System.Collections.Generic;
using Demo.Business;
using Demo.Presentation;
using NUnit.Framework;
using Rhino.Mocks;
using StructureMap;
using StructureMap.Graph;

namespace Demo.UnitTests.Presentation
{
    [TestFixture]
    public class When_editing_a_person
    {
        private IPersonRepository repository;
        private List<Person> allPeople;
        private IEditPersonView view;
        private EditPersonPresenter presenter;

        [SetUp]
        public void Arrange()
        {
            repository = MockRepository.GenerateMock<IPersonRepository>();
            allPeople = new List<Person>();
            view = MockRepository.GenerateMock<IEditPersonView>();
            SetupStructureMap();
            
        }

        private void SetupStructureMap()
        {
            ObjectFactory.Initialize(
                registry => registry.Scan(x =>
                                              {
                                                  x.AssemblyContainingType<IPersonRepository>();
                                                  registry.ForRequestedType<IPersonRepository>().TheDefault.Is.Object(
                                                      repository);
                                                      
                                              }));

        }

        public void Act()
        {
            presenter = new EditPersonPresenter(view, repository);
        }
        [Test]
        public void all_people_is_set_to_all_people_from_the_repo_on_initial_load()
        {
            repository.Stub(r => r.GetAll()).Return(allPeople);
            view.Stub(v => v.IsInitialLoad).Return(true);
            Act();
            
            view.AssertWasCalled(v => v.AllPeople = allPeople);
        }
        [Test]
        public void when_a_person_is_selected_the_view_is_bound_to_it()
        {
            //Add a person to the repo
            var person = new Person {Name = "Test Person"};
            allPeople.Add(person);
            view.Stub(v => v.SelectedPersonId).Return(person.Id);
            repository.Stub(r => r.GetById(person.Id)).Return(person);
            
            Act();
            view.Raise(v => v.PersonSelected += null,null,EventArgs.Empty);

            view.AssertWasCalled(v => v.Name = person.Name);
        }
        [Test]
        public void when_the_user_clicks_save_the_person_is_persisted()
        {
            //Add a person to the repo
            var person = new Person { Name = "Test Person" };
            allPeople.Add(person);
            repository.Stub(r => r.GetById(person.Id)).Return(person);
            
            view.Stub(v => v.SelectedPersonId).Return(person.Id);
            var theName = "Modified";
            view.Stub(v => v.Name).Return(theName);

            Act();
            view.Raise(v => v.PersonSaved += null,null,EventArgs.Empty);

            var personSentToRepo = (Person)repository.GetArgumentsForCallsMadeOn(r => r.Save(person))[0][0];
            Assert.AreEqual(person.Id,personSentToRepo.Id);
            Assert.AreEqual(theName,personSentToRepo.Name);

        }
        [Test]
        public void you_can_not_save_2_people_with_the_same_name()
        {
            
            var person1 = new Person {Name = "Some Dude"};
            allPeople.Add(person1);
            var person2 = new Person {Name = "Some Other Dude"};
            allPeople.Add(person2);
            repository.Stub(r => r.GetAll()).Return(allPeople);
            repository.Stub(r => r.GetById(person1.Id)).Return(person1);

            view.Stub(v => v.SelectedPersonId).Return(person1.Id);
            // now set the views name to the name of the other person
            view.Stub(v => v.Name).Return(person2.Name);

            Act();
            view.Raise(v => v.PersonSelected += null,null,EventArgs.Empty);
            
            Assert.IsFalse(presenter.Valid);

        }
        [Test]
        public void AllPeople_is_not_set_if_the_view_is_a_postback()
        {
            repository.Stub(r => r.GetAll()).Return(allPeople);
            view.Stub(v => v.IsInitialLoad).Return(false);
            Act();
            view.AssertWasNotCalled(v => v.AllPeople = allPeople);
        }
        [Test]
        public void when_PersonAdded_is_raised_a_new_person_is_added_to_the_repo()
        {
            var theNewName = "The New Person";
            view.Stub(v => v.Name).Return(theNewName);
            
            Act();
            view.Raise(v => v.PersonAdded += null,null,EventArgs.Empty);

            var thePersonSaved = (Person)repository.GetArgumentsForCallsMadeOn(r => r.Save(null), o => o.IgnoreArguments())[0][0];
            Assert.AreEqual(theNewName,thePersonSaved.Name);
        }
        [Test]
        public void when_a_person_is_added_AllPersons_is_repopulated()
        {
            repository.Stub(r => r.GetAll()).Return(allPeople);
            Act();
            view.Raise(v => v.PersonAdded += null,null,EventArgs.Empty);
            view.AssertWasCalled(v=> v.AllPeople = allPeople);
        }
        [Test]
        public void when_a_person_is_saved_AllPeople_is_updated()
        {
            var someDude = new Person {Name = "Some Dude"};
            allPeople.Add(someDude);
            repository.Stub(r => r.GetById(someDude.Id)).Return(someDude);
            repository.Stub(r => r.GetAll()).Return(allPeople);
            view.Stub(v => v.SelectedPersonId).Return(someDude.Id);
            view.Stub(v => v.Name).Return("Edited");
            Act();
            view.Raise(v => v.PersonSaved+= null,null,EventArgs.Empty);
            view.AssertWasCalled(v => v.AllPeople = allPeople);
        }
    }
}